/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef TODO_TYPES_H
#define TODO_TYPES_H
#include <vector>
#include <map>
#include <deque>
#include <string>
#include "config.h"
#include "exceptions.h"
#include "internal.h"

namespace todo {
	
class Type;

class TypeManager {
	typedef std::map<std::string, Type*> map_types;
	map_types types;
public:
	TypeManager();
	~TypeManager();
	
	// Register a type
	void register_type(const std::string&, Type*) throw (todo::Exception);
	
	// Returns a type if registered
	Type* get_type(const std::string&) throw (todo::Exception);
};

class Value {
public:
	std::map<std::string, Value*> properties;
	Value* parent;
	Type* type;
	void* data;
	
	Value() : parent(NULL), type(NULL), data(NULL) { }
	Value(Type* n_type, void* n_data, Value* n_parent = NULL) : parent(n_parent), type(n_type), data(n_data) { }
	
	Value* get_property (const std::string&) throw (todo::Exception);
};

class Type {
protected:
	typedef std::vector<MethodBase*> base_method_map;
	typedef std::map<std::string, base_method_map> method_map;
	
	// The type name
	std::string name;
	
	// Methods
	method_map methods;

	// Type attributes
	std::map<std::string, Type*> properties;
	
	// The parent if has
	Type* parent;

	// Either the type is abstract or not
	bool abstract;

public:
	Type(const std::string& the_name, Type* the_parent = NULL, bool is_abstract = false) : 
		name(the_name), parent(the_parent), abstract(is_abstract) {}
	
	~Type();
	
	// Register a method
	void register_method(MethodBase*) throw (todo::Exception);
	
	// Register a property
	void register_property(const std::string&, Type*) throw (todo::Exception);

	// Calls method m_name within object value with arguments args
	MethodBase *find_method_ptr (const std::string&, const ArgValues*);
	MethodBase *find_method_ptr (const std::string& , const Type** , const unsigned int);

	Value* call_method(const std::string&, Value*, const ArgValues*, std::string cand = "") throw (todo::Exception);
	Value* call_static_method(const std::string&, const ArgValues*, std::string cand = std::string("")) throw (todo::Exception);
	
	Method* get_method(const Type*, const std::string&, const ArgValues*, std::string&) throw (todo::Exception);
	
	const std::string& get_name() const { return name; }
	
	void alloc_value_properties(Value*);

	bool is_child_of(Type*) const;

	Type** type_list(unsigned int num, ...);
	
	// Pure virtual methods
	virtual Value* toString(Value*, const ArgValues*) = 0;
	virtual Value* alloc() = 0;
	virtual Value* copy(const Value*) = 0;
	virtual void dealloc(Value*) = 0;
	virtual void load() = 0;	
};

enum DataType {
	TODO_UNUSED,
	TODO_VAR,
	TODO_CONSTANT,
	TODO_RET_VALUE
} __attribute__((packed));

/**
 * Structs that hold a returned value
 */
struct ValueResult {
	Value* value;
	
	ValueResult() : value(NULL) { }
	~ValueResult();
	
	void set_value(Value*);
};

enum ValueResultFlag {
	NONREFERENCED, // An allocated pointer
	REFERENCED     // An pointer to already allocated pointer
} __attribute__((packed));

struct Data {
	DataType data_type;
	ValueResultFlag result_flag;
	unsigned int extra;
	Value* data;
	Type* type;
	ValueResult* result;
	
	Data() : 
		data_type(TODO_UNUSED), result_flag(NONREFERENCED), extra(0), data(NULL), type(NULL), result(NULL) { }
	
	Data(DataType type_) : 
		data_type(type_), result_flag(NONREFERENCED), extra(0), data(NULL), type(NULL), result(NULL) { }
	
	Data(DataType type_, unsigned int extra_) : 
		data_type(type_), result_flag(NONREFERENCED), extra(extra_), data(NULL), type(NULL), result(NULL) { }
		
	Data(DataType type_, Value* data_, unsigned int extra_) : 
		data_type(type_), result_flag(NONREFERENCED), extra(extra_), data(data_), type(NULL), result(NULL) { }
	
	Data(const Data&);
	
	void setDataType(DataType type_) { data_type = type_; }
	DataType getDataType() const { return data_type; }
};

} // todo

extern todo::TypeManager typeman;

#endif // TODO_TYPES_H
